import { ArrayMethods } from "./arr"
export function observer(data) {
  /**
   * data情况分析
   * (1)对象 { a:{b:1},c:1}
   * (2)数组  { list:[1,2,3],arr:[{a:1,b:2}]}
   *
   */
  // 1.判断:如果不是对象或者为空,不进行劫持

  if (typeof data != "object" || data == null) {
    return data
  }

  //   情况1,是个对象,进行劫持(defineProperty方式+遍历+递归)
  return new Observer(data)
  // 情况2,对数组进行数据劫持, list:[1,2,3] list:[{a:1,b:2}]
  //方法函数劫持,重写数组方法, push,splice,pop
}
// vue2数据劫持底层: Object.defineProperty
//缺点:只能对对象中的某个属性进行劫持.{a:1,b:2}=>只能观测a或b
class Observer {
  constructor(value) {
    // 给数组定义一个属性
    Object.defineProperty(value, "__ob__", {
      enumerable: false, //不可枚举
      value: this, //this为observer实例
    })
    // 判断是数组还是对象
    if (Array.isArray(value)) {
      // 对普通数组进行数据劫持 : 例如 [1,2,3]
      value.__proto__ = ArrayMethods //给传递的数组原型链上的数组方法改成重写后的
      // 对对象数组进行数据劫持: 例如 [{a:1,b:2}]
      this.observerArray(value)
    } else {
      //由于只能观测一个,而data中都是响应式,所以采用遍历进行劫持
      this.walk(value) //用于遍历属性,劫持属性
    }
  }
  walk(data) {
    let keys = Object.keys(data) //获取对象中所有属性的key
    for (let i = 0; i < keys.length; i++) {
      // 遍历所有属性,对属性进行劫持
      let key = keys[i]
      let value = data[key] //获取属性值
      defineReactive(data, key, value) //对对象中的某个属性进行劫持
    }
  }
  observerArray(value) {
    for (let i = 0; i < value.length; i++) {
      observer(value[i])
    }
  }
}
//对对象中的某个属性进行劫持
function defineReactive(data, key, value) {
  // value:可能是一层对象,也可能是多层
  // 1.深度代理:判断value是否为对象,如果是对象,进行递归劫持,
  observer(value)
  Object.defineProperty(data, key, {
    get() {
      console.log("获取")
      // 获取当前对象中该值时触发
      return value //返回值
    },
    set(newValue) {
      // 判断值是否与原值是否相同
      if (newValue === value) return
      //   console.log("设置")
      observer(newValue) //对新值进行劫持,否则,当 a={c:3}的时候,会失去对c的劫持
      value = newValue //设置值
    },
  })
}
